Coding for DS and DM
R coding module

Lecture 9

Andrea Cappozzo
andrea.cappozzo@unimi.it
AndreaCappozzo
andreacappozzo.rbind.io

R as an OOP language

  • As we have seen so far, R is at its heart a functional programming language
    1. Everything that exists is an object.
    2. Everything that happens is a function call.
  • Nonetheless, R has some class systems that allow for Object Oriented Programming (OPP)
    • S3
    • S4
    • S7

OOP in R

  • OOP aims at creating new ‘’entities’’ incorporating tasks we want to bring together for a specific purpose.
  • So objects are created having certain characteristics (attributes) for doing something (methods).
  • In Python, object methods are easily recognized as they are typically invoked using a dot, e.g., pandas_name.read_csv, where pandas_name (often named pd) is a pandas object, and read_csv is its method.
  • In R we have the symbol ’‘$’’ for separating the name of the object and the attribute or the method.

S3 class

  • S3 is the simplest yet the most popular OOP system in R.
  • To check if an object is an S3 object you can use is.object() or otype() from the sloop package.
df <- data.frame(x = 1:10, y = letters[1:10])
is.object(df)
[1] TRUE
sloop::otype(df)
[1] "S3"
class(df)
[1] "data.frame"

S3 class

  • Now we want to create the method adding_1
  • Such a method should behave differently depending on the S3 class
  • First of all we have to define the default method which states what the method should do, i.e. adding 1 to each element and display the result:
adding_1 <- function(x, ...) UseMethod("adding_1")
adding_1.default <- function(x, ...){
  x <- x + 1
  x
}
  • UseMethod defines the name of the method.

S3 class

  • Now we want to define what the method adding_1 should do in case we have a list data structure:
adding_1.list <- function(x, ...){
  num <- sapply(x, is.numeric)
  x[num] <- lapply(x[num], adding_1)
  x
}
  • This method will be used when applying adding_1 to lists

S3 class

  • Now we want to define what the method adding_1 should do in case we have a data.frame data structure
adding_1.data.frame <- function(x, ...){
  num <- sapply(x, is.numeric)
  x[num] <- lapply(x[num], adding_1)
  x
}

adding_1 with integer

  • We can check that adding_1 behaves differently depending on the S3 class
x <- 1:10
class(x)
[1] "integer"
adding_1(x)
 [1]  2  3  4  5  6  7  8  9 10 11

adding_1 with data.frame

tibble::glimpse(df)
Rows: 10
Columns: 2
$ x <int> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
$ y <chr> "a", "b", "c", "d", "e", "f", "g", "h", "i", "j"
class(df)
[1] "data.frame"
adding_1(df)
    x y
1   2 a
2   3 b
3   4 c
4   5 d
5   6 e
6   7 f
7   8 g
8   9 h
9  10 i
10 11 j

Create your own S3 class

  • A class can be assigned to a object by just adding an attribute to it
Match_list <- list(name = "Inter-Roma", score="0-1", referee = "Maresca")
class(Match_list) <- "Match"
Match_list
$name
[1] "Inter-Roma"

$score
[1] "0-1"

$referee
[1] "Maresca"

attr(,"class")
[1] "Match"

Create your own S3 class

  • And then you can define a particular behavior of adding_1 to this newly created class
adding_1.Match <- function(x, ...){
  score <- x$score
  score_tmp <- strsplit(score, "-")[[1]]
  score_p_1 <- as.numeric(score_tmp) + 1
  paste(score_p_1, collapse = "-") # Combine back into the "x-y" format
}

Create your own S3 class

  • And then you can define a particular behavior of adding_1 to this newly created class
adding_1(Match_list)
[1] "1-2"

S3: much more to talk about

  • This has just given you the intuition of the power of the S3 OO system
  • Good references to know more about can be found here and here
  • Some methods you should always consider adding to your own class:
    • print
    • summary
    • plot (or autoplot)
    • predict (for ML algorithms)

Final Meme